<?php

    /**********************************************************************************
     * LeafPHP 图像处理类 www.leafphp.com
     * --------------------------------------------------------------------------------
     * 该类可从框架中提取，独立使用（需要定义file_exists_case函数，即大小写敏感的检测文件是否存在）
     * 对外主要提供如下方法
     * getImageExt()：获取图像的拓展名
     * getCode()    ：获取验证码
     * imageThumb() ：生成图片的缩略图
     * imageXy()    ：图像翻转
     * imageAngle() ：图像按角度旋转
     * -------------------------------------------------------------------------------
     * 在LeafPHP框架中的使用方法，以获取验证码为例
     * load(__DRIVER_DIR__.__SP__.'class.image.php');  //加载image类
     * $image = image::getInstance();                  //实例化image对象
     * $image->imageCode();                            //调用image对象中的方法
     * --------------------------------------------------------------------------------
     * CSS设置图片旋转
     * img.rotated90 {
     * rotation: 90deg;
     * -webkit-transform: rotate(90deg);
     * -moz-transform: rotate(90deg);
     * filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1);
     * }
     * --------------------------------------------------------------------------------
     * $Author:hongshuaidong
     * $Dtime:2014-02-22
     *********************************************************************************/

    if (!defined('__IS_LEAF__')) exit('Access Denied!');

    class image {

        static private $obj             = null;         //当前的对象，用于单例模式
        static private $error           = false;        //当前的错误信息

        public         $checkCodeWidth  = 80;           //图片验证码的宽度
        public         $checkCodeHeight = 22;           //图片验证码的告诉

        private        $imageExt        = null;         //常用图片拓展

        /**
         * 构造方法
         * @access private
         */
        private function __construct(){

            //检测gd库
            $this->checkGd();
            $this->init();

        }

        /**
         * 单例模式，实例化自身对象
         * @access public
         * @return object 返回自身实例化的对象
         */
        static public function getInstance(){

            //如果已经实例化，则返回
            if(self::$obj){
                return self::$obj;
            }
            //如果没有实例化，则实例化自身对象，如果当前类检测到错误，则返回错误信息
            else{
                //实例化自身对象
                self::$obj = new static();
                //如果有错误，则将错误信息返回
                if( self::$error ) return self::$error;
                return self::$obj;
            }

        }

        /**
         * 检测gd库是否安装
         * @access private
         */
        private function checkGd(){
            //检测gd库是否安装
            $extensions = get_loaded_extensions();
            if( !in_array('gd', $extensions) ){
                self::$error = L('gd_not_open');
            }
        }

        /**
         * 实例化当前类为对象时，调用的初始化方法
         * @access private
         * @return 该函数没有返回值
         */
        private function init(){

            //初始化图像的所有拓展与常理的关联
            $this->imageExt = array(
                "gif"  => IMAGETYPE_GIF,
                "jpg"  => IMAGETYPE_JPEG,
                "jpeg" => IMAGETYPE_JPEG,
                "png"  => IMAGETYPE_PNG,
                "swf"  => IMAGETYPE_SWF,
                "psd"  => IMAGETYPE_PSD,
                "bmp"  => IMAGETYPE_BMP,
                "tiff" => IMAGETYPE_TIFF_II,
                "tiff" => IMAGETYPE_TIFF_MM,
                "jpc"  => IMAGETYPE_JPC,
                "jp2"  => IMAGETYPE_JP2,
                "jpx"  => IMAGETYPE_JPX,
                "jb2"  => IMAGETYPE_JB2,
                "swc"  => IMAGETYPE_SWC,
                "iff"  => IMAGETYPE_IFF,
                "wbmp" => IMAGETYPE_WBMP,
                "xbm"  => IMAGETYPE_XBM,
                "ico"  => IMAGETYPE_ICO
                 );
        }

        /**
         * 获取图像拓展名，支持远程图片文件，如：http://su.bdimg.com/static/superpage/img/logo_white.png
         * @access public
         * @param  string  $sourceImage  源图像的路径
         * @return bool    如果图片源正确，则返回图片拓展名，否则返回false
         */
        public function getImageExt($sourceImage){

            if( @$fh = fopen($sourceImage, 'r') ){
                fclose($fh);
                $imageExt = mb_substr($sourceImage, strrpos($sourceImage, '.'));
                return trim($imageExt, '.');
            }else{
                return false;
            }

        }

        /**
         * 设置图像在浏览器的输出信息
         * @access private
         * @param  string  $imageType 想要设置的的图片的类型
         * @return boolean 通常返回true，如果$imageType参数有误，则返回false
         * @return 该函数没有返回值
         */
        private function imageHeader($imageType){

            if( array_key_exists($imageType, $this->imageExt) ) {
                header("Content-type: " . image_type_to_mime_type($this->imageExt[$imageType]));
                return true;
            }
            return false;

        }

        /**
         * 获取图像验证码
         * Tips：
         * 1、使用此方法，生成验证码，可验证$_SESSION['checkCode']与用户输入的验证码的值来判断用户输入是否正确。
         * 2、验证码，嵌入到html页面的验证码，可这样写：<img src="you image url" onclick="this.src='your image url'+Math.random()" />
         * @access public
         * @param  int      $length  验证码位数，建议4-6位
         * @param  int      $type    验证码类型，0为数字，1为字符串小写，2为字符串大写，3为小写字符串与数字混合，4为大写字符串与数字混合，5为全混
         * @param  string   $imgType  验证码图片类型，可设置多种，如jpeg、png、gif，所有可选，可参考$this->imageExt属性的键
         * @return 该函数没有返回值，直接输出生成的图像
         */
        public function imageCode($length=4, $type=0, $imgType='png'){
            
            //获取要随机的字符串源
            switch ($type) {
                case 0:
                    $str = str_shuffle($str = '0123456789');
                    break;
                case 1:
                    $str = str_shuffle($str = 'abcdefghijkmnpqrstuvwxyz');
                    break;
                case 2:
                    $str = str_shuffle($str = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ');
                    break;
                case 3:
                    $str = str_shuffle($str = 'abcdefghijkmnpqrstuvwxyz1234567890');
                    break;
                case 4:
                    $str = str_shuffle($str = 'ABCDEFGHIJKLMNPQRSTUVWXYZ1234567890');
                    break;
                default:
                    $str = str_shuffle($str = 'abcdefghijkmnpqrstuvwxyzABCDEFGHIJKLMNPQRSTUVWXYZ1234567890');
                    break;
            }
            //获取随机位数的字符串
            $checkCode = substr( $str, rand( 0, strlen($str)-$length ), $length);
            $checkCodeArr = array();
            for ($i=0; $i < $length; $i++) { 
                $checkCodeArr[] = $checkCode[$i];
            }
            //将验证码保存于session中
            $_SESSION['checkCode'] = $checkCode;
            //绘图
            //1、创建画布
            $imageSource = imagecreatetruecolor($this->checkCodeWidth, $this->checkCodeHeight);
            //2、为画布分配颜色
            $bgColor = imagecolorallocate($imageSource, 248, 248, 248);
            imagefill($imageSource, 0, 0, $bgColor);
            //3、添加斑点，更改斑点数量，更改for循环次数即可
            //获取干扰像素的量级
            $PixelNum = ($this->checkCodeWidth * $this->checkCodeHeight )/6;
            for ($i=0; $i < $PixelNum; $i++) {
                $pixelColor = imagecolorallocate($imageSource, rand(150,255), rand(150,255), rand(0,255));
                imagesetpixel($imageSource, rand(0, $this->checkCodeWidth), rand(0,$this->checkCodeHeight), $pixelColor);
            }
            //4、添加干扰线
            //获取干扰线量级
            $lineNum = ($this->checkCodeWidth * $this->checkCodeHeight )/250;
            for ($i=0; $i < $lineNum; $i++) {
                $lineColor = imagecolorallocate($imageSource, rand(200,250), rand(100,250), rand(200,250));
                imageline($imageSource, rand(0, $this->checkCodeWidth), rand(0,$this->checkCodeHeight), rand(0, $this->checkCodeWidth), rand(0,$this->checkCodeHeight), $lineColor);
            }
            //2、在画布上绘出字符串
            foreach ($checkCodeArr as $key => $value) {
                //获取字符在横坐标上可移动的范围的一个具体值
                $offsetX = $key*($this->checkCodeWidth/$length)+4+ rand(-5, 5) ;
                //获取字符在纵坐标上可移动的范围的一个具体值，即图片高度-字符高度
                $offsetY = rand(0,$this->checkCodeHeight-13);
                $strColor = imagecolorallocate($imageSource, rand(0,250), rand(0,50), rand(0,50));
                imagechar($imageSource, 5, $offsetX, $offsetY, $value, $strColor);
            }
            //设置图像输出头信息
            $this->imageHeader();($imgType);
            //输出图像
            $imageFunc = 'image'.$imgType;
            $imageFunc($imageSource);
            //销毁图像的内存占用
            imagedestroy($imageSource);

        }

        /**
         * 创建缩略图：将源图像，生成等比例的缩略图
         * 默认是原图缩放，比如原图为png格式，则生成的缩略图也是png格式，不过输出格式可自由定制
         * @access public
         * @param  string  $sourceImagePath  源图像路径
         * @param  string  $width            生成的缩略图的宽度
         * @param  string  $height           生成的缩略图的高度
         * @param  string  $imageOutType     生成的缩略图的类型(可实现图片类型转换)
         * @return 该函数没有返回值，直接输出生成的图像
         */
        public function imageThumb($sourceImagePath, $width=null, $height=null, $imageOutType=null){

            //参数判断
            //1、判断文件是否存在，并获取文件拓展名
            if( !$imageExt = $this->getImageExt($sourceImagePath) ){
                return '设置的图像路径有误！';
            }
            //2、判断是否设置要生成的缩略图的宽度和高度
            if( $width==0 && $height==0  ){
                return '需要设置要生成的缩略图的宽度或者高度的至少其中一个，且值要大于0！';
            }
            if($imageExt=='jpg') $imageExt='jpeg';

            //创建源图像
            //1、根据源图像拓展名，自动动态创建图像源，所以需要可变函数
            $imagecreate = 'imagecreatefrom'.$imageExt;
            //2、创建图像源
            $imageSource = $imagecreate($sourceImagePath);
            
            //获取源图像信息
            $imageSourceInfo = getimagesize($sourceImagePath);

            //生成缩略图
            //1、获取要生成的缩略图的宽度和高度（如果仅仅设置宽度，则等比例获取高度，如果仅仅设置高度，则等比例获取宽度）
            if($width>0 && $height==0)
            {
                $height = (int) $height =  $imageSourceInfo[1] * ( $width/$imageSourceInfo[0] );
            }
            elseif($width==0 && $height>0)
            {
                $width = (int) $width = $imageSourceInfo[0] * ( $height/$imageSourceInfo[1] );
            }
            //2、创建一幅空白图像，宽度和高度是缩略图的宽度和高度，用于放置缩略图内容信息
            $imageThumb = imagecreatetruecolor($width, $height);

            //3、将源图像的部分或全部，拷贝到空白图像中，相比于函数imagecopyresized，下面的得到的缩略图更清晰
            imagecopyresampled($imageThumb, $imageSource, 0, 0, 0, 0, $width, $height, $imageSourceInfo[0], $imageSourceInfo[1]);
            
            //输出图片信息
            //1、钉子户输出图片的格式
            if( !is_null($imageOutType) )
            {
                $imageOutType = ($imageOutType=='jpg') ? 'jpeg' : $imageOutType;
                $imageExt =  in_array( $imageOutType, array('jpeg', 'gif', 'png') ) ? $imageOutType : $imageExt;
            }
            $imageout    = 'image'.$imageExt;
            //2、输出图片mime信息
            $this->imageHeader($imageExt);
            //3、图片内容输出
            $imageout($imageThumb);

        }

        /**
         * 图像翻转函数，此函数可对图片实现 水平翻转、垂直翻转、同时翻转
         * 注意：
         * 1、此函数不能讲图片，顺时针或逆时针，按角度翻转！若想实现此种翻转，请参数图片旋转方法imageAngle()
         * 2、如果需要前端实现图片翻转，最好使用css的方式完成。
         * @access public
         * @param  string  $sourceImage  源图像路径
         * @param  int     $type         翻转类型，1为水平翻转，2为垂直翻转，3为两者都有
         * @param  string  $imageOutType 输出图片的类型（可用于将jpg图转为png图等），可选参数为jpeg、gif、png
         * @return 该函数没有返回值，直接输出生成的图像
         */
        public function imageXy($sourceImage, $type, $imageOutType=null){

            //判断文件是否存在，并获取文件拓展名
            if( !$imageExt = $this->getImageExt($sourceImage) ){
                return false;
            }
            if($imageExt=='jpg') $imageExt='jpeg';

            //设置可变函数
            //1、创建图片资源的可变函数
            $imagecreate = 'imagecreatefrom'.$imageExt;
            //2、图片输出的可变函数：如果定制了图片输出格式并且格式正确，则按照用户定制输出，否则按照原图片格式输出
            if( !is_null($imageOutType) )
            {
                $imageOutType = ($imageOutType=='jpg') ? 'jpeg' : $imageOutType;
                $imageExt =  in_array( $imageOutType, array('jpeg', 'gif', 'png') ) ? $imageOutType : $imageExt;
            }
            $imageout    = 'image'.$imageExt;

            //创建图片资源
            $im = $imagecreate($sourceImage);
            imagealphablending($im, true); 
            imagesavealpha($im, true); 

            //根据条件，翻转图像
            if($type==1)
            {
                //水平翻转
                imageflip($im, IMG_FLIP_HORIZONTAL);
            }
            elseif($type==2)
            {
                //垂直翻转
                imageflip($im, IMG_FLIP_VERTICAL);
            }
            else
            {
                //水平、垂直翻转
                imageflip($im, IMG_FLIP_BOTH);
            }

            //设置图片mime类型
            $this->imageHeader($imageExt);

            //输出图像
            $imageout($im);
            //销毁图像在内存中的占用
            imagedestroy($im);

        }

        /**
         * 图像翻转函数，可对图片，按角度翻转，可定制输出图片格式。
         * 此函数还可实现图片的格式转换，比如1.jpg图转成png图，则imageAngle('1.jpg', 0, 'png');
         * Tips：
         * 1、此函数不能对图片实现水平和垂直方向的翻转，若想实现此种翻转，请参数imageXy()方法
         * 2、旋转方式为：旋转的中心是图像的中心，旋转后的图像会按比例缩小以适合目标图像的大小——边缘不会被剪去。
         * 3、PNG格式的图片，可以旋转之后，保留旋转间隙为透明色。将imagerotate中的0改为127即可。
         * 4、旋转后，图片尺寸可能发生变化。视旋转角度而定。
         * 5、如果需要前端实现图片翻转，最好使用css的方式完成。
         * @access public
         * @param  string  $sourceImage  源图像路径
         * @param  int     $type         翻转类型，1为水平翻转，2为垂直翻转，3为两者都有
         * @param  string  $imageOutType 输出图片的类型（可用于将jpg图转为png图等），可选参数为jpeg、gif、png
         * @return 该函数没有返回值，直接输出生成的图像
         */
        public function imageAngle($sourceImage, $angle, $imageOutType=null){

            //检测角度参数
            if(!is_int($angle)){
                return '角度参数设置不为整数！';
            }

            //判断文件是否存在，并获取文件拓展名
            if( !$imageExt = $this->getImageExt($sourceImage) ){
                return '设置的图像路径有误！';
            }
            if($imageExt=='jpg') $imageExt='jpeg';


            //设置可变函数
            //1、创建图片资源的可变函数
            $imagecreate = 'imagecreatefrom'.$imageExt;
            //2、图片输出的可变函数：如果定制了图片输出格式并且格式正确，则按照用户定制输出，否则按照原图片格式输出
            if( !is_null($imageOutType) )
            {
                $imageOutType = ($imageOutType=='jpg') ? 'jpeg' : $imageOutType;
                $imageExt =  in_array( $imageOutType, array('jpeg', 'gif', 'png') ) ? $imageOutType : $imageExt;
            }
            $imageout = 'image'.$imageExt;

            //创建图片资源
            $im = $imagecreate($sourceImage);

            //根据角度，旋转图像，无法覆盖的区域，用白色填补（如果想把透明色改为透明色，请把下面一行代码的0改为127）
            $rotation = imagerotate($im, $angle, imageColorAllocateAlpha($im, 255, 255, 255, 0));
            imagealphablending($rotation, false); 
            imagesavealpha($rotation, true);

            //设置图片mime类型
            $this->imageHeader($imageExt);

            //输出图像
            $imageout($rotation);

            //销毁图像在内存中的占用
            imagedestroy($im);

        }

    }

?>